package main
// use
import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"

	"github.com/gorilla/websocket"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	stdin = iota
	stdout
	stderr
)

var cacheBuf bytes.Buffer

func main() {

	kubeconfig := "./config"
	config, _ := clientcmd.BuildConfigFromFlags("", kubeconfig)

	tlsConfig, err := rest.TLSConfigFor(config)

	tlsConfig.InsecureSkipVerify = true
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Println(config.Host)

	u, err := url.Parse(config.Host)
	if err != nil {
		log.Fatalln(err)
	}

	switch u.Scheme {
	case "https":
		u.Scheme = "wss"
	case "http":
		u.Scheme = "ws"
	default:
		log.Fatalln("err")
	}

	u.Path = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/exec", "hx", "my-mysql")

	rawQuery := "stdout=true&tty=true&command=sh&container=mysql&stdin=true"
	u.RawQuery = rawQuery

	request := &http.Request{
		Method: http.MethodGet,
		URL:    u,
	}

	var protocols = []string{
		"v4.channel.k8s.io",
		"v3.channel.k8s.io",
		"v2.channel.k8s.io",
		"channel.k8s.io",
	}
	dialer := &websocket.Dialer{
		Proxy:           http.ProxyFromEnvironment,
		TLSClientConfig: tlsConfig,
		Subprotocols:    protocols,
	}
	// defaultDialer := websocket.DefaultDialer
	conn, _, err := dialer.Dial(request.URL.String(), request.Header)
	if err != nil {
		log.Fatalln(err)
	}
	defer conn.Close()

	err = WebsocketHandler(conn)
	if err != nil {
		log.Fatalln("error occure", err)
	}
}

func WebsocketHandler(c *websocket.Conn) error {
	errChan := make(chan error, 3)
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		buf := make([]byte, 1025)
		for {
			n, err := os.Stdin.Read(buf[1:])
			if err != nil {
				errChan <- err
				return
			}

			cacheBuf.Write(buf[1:n])
			cacheBuf.Write([]byte{13, 10})
			if err := c.WriteMessage(websocket.BinaryMessage, buf[:n+1]); err != nil {
				errChan <- err
				return
			}
		}
	}()

	go func() {
		defer wg.Done()
		for {
			_, buf, err := c.ReadMessage()
			if err != nil {
				errChan <- err
				return
			}

			if len(buf) > 1 {
				var w io.Writer
				switch buf[0] {
				case stdout:
					w = os.Stdout
				case stderr:
					w = os.Stderr
				}

				if w == nil {
					continue
				}
				s := strings.Replace(string(buf[1:]), cacheBuf.String(), "", -1)
				_, err = w.Write([]byte(s))
				if err != nil {
					errChan <- err
					return
				}
			}
			cacheBuf.Reset()
		}
	}()

	wg.Wait()
	close(errChan)
	err := <-errChan
	return err
}